﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

/*
*/
namespace Ruandao.MiniFace
{
    using Ruandao.Misc;
    using System.Xml.Linq;
    using System.Configuration;
    using System.IO;

    /// <summary>
    /// 加载一个themes.xml文件。
    /// </summary>
    /// <remarks>
    /// <![CDATA[
    /// <themes defaultTheme="">
    /// -- themes.xml的根元素，一个themes.xml文件表示了网站在某个位置存放的所有主题（外观）。
    /// -- defaultTheme表示默认的Theme名称。
    /// -- 属性名不使用default是由于default是C#中的关键字，不方便用dynamic编程。
    /// -- 文件名始终是themes.xml，不能设为其他的名称。
    /// 
    ///      <theme name="" title="" image="" > 
    ///      -- name表示主题的唯一名称。
    ///      -- title表示主题的可读性标题。
    ///      -- image表示主题示意图的文件，位于folder所在目录。
    ///      
    ///         <component name="" location="" skin=""  />
    ///         -- component可以重复多个，表示该主题所使用的可以加载到页面中的组件。
    ///         -- name：必填属性。表示组件名称， name名称的作用可以在下面的group中被引用。
    ///         -- name的取值可以和skins.xml所在文件夹的名称不相同。    
    ///         -- location：必填属性。指出skins.xml所在文件夹的位置，可以使用完全路径（http://...）、基于网站根的路径（/...）、
    ///         -- 基于应用程序的路径（~/...）和相对于themes.xml所在位置的路径（.../...）。
    ///         -- skin：可选属性，表示该组件中所选择的皮肤名称，如果缺省或者如果指定皮肤不存在则表示使用默认皮肤。
    ///             
    ///         <group name="" components="-,-,-"/>
    ///         -- group元素是可选的。
    ///         -- 给component归组，便于一次加载多个component皮肤，group的举例如：common、pages、local等等。
    ///         -- 以逗号分隔的component的名称。
    ///      </theme> 
    /// 
    /// </themes>
    /// ]]>
    /// </remarks>
    public class Theme
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public string Image { get; set; }

        /// <summary>
        /// 主题所包含的所有组件的组件名称，以及该组件所需要加载的所有引用文件。
        /// key：组件名称，value为需要加载的所有引用文件。
        /// </summary>
        public IDictionary<string, IList<dynamic>> Components { get; private set; }

        /// <summary>
        /// 皮肤组件的分组。key：组名称，value组件名称。
        /// </summary>
        public IDictionary<string, IList<string>> Groups { get; private set; }

        public Theme()
        {
            this.Components = new Dictionary<string, IList<dynamic>>();
            this.Groups = new Dictionary<string, IList<string>>();
        }
    }

    public class Themes
    {
        private static Dictionary<string, Themes> cacheThemesInstance = new Dictionary<string, Themes>();

        public static Themes GetInstance(string path)
        {
            if (!cacheThemesInstance.ContainsKey(path))
            {
                cacheThemesInstance.Add(path, new Themes(path));
            }
            return cacheThemesInstance["path"];
        }

        private string themesPath; // 支持网络地址。记录themes.xml所在的位置。
        private string defaultTheme { get; set; }

        private Dictionary<string, Theme> allThemes = new Dictionary<string, Theme>();

        /// <summary>
        /// 读取某个主题（即某个themes.xml文件）中的所有样式信息。
        /// </summary>
        /// <param name="path">themePath指出的是themes.xml的定位,支持http://、~/和/打头的三种路径。
        /// 注意，不支持~打头的路径。</param>
        private Themes(string path)
        {
            this.themesPath = UrlUtility.ClientUrl(path);

            var doc = XDocument.Load( UtilityFunction.CombineUrl(this.themesPath,"themes.xml"));
            dynamic themes = doc.Element("themes").AsDynamic();
            
            foreach (var theme in ConvertToList(themes.theme))
            {
                Theme theTheme = new Theme();
                theTheme.Name = theme.name;
                theTheme.Title = theme.title;
                theTheme.Image = theme.image;

                // 遗留问题：上面foreach总能正确的工作，不论子元素的个数是否为一个。
                // 不知道为什么当只有一个component元素时，使用group.component无法正确获取子元素
                // 所以在下面的循环中使用了group.Nodes的语法。
                // 可能的原因是DynamicXmlNode中存在bug，但仔细检查后始终没能发现。
                foreach (var sub in ConvertToList(theme.Nodes))
                {
                    if (sub.Name == "component")    
                    {
                        string skinPath = UrlUtility.ClientUrl(sub.location, this.themesPath);
                        Skins skins = Skins.GetInstance(skinPath);
                        Skin  skin  = skins.GetSkin(sub.skin);
                        List<dynamic> list = new List<dynamic>(skins.CommonFiles);
                        list.AddRange( skin.ReferenceFiles );
                        theTheme.Components.Add( sub.name,  list ); // 注意：sub.name和sub.Name是不同的。
                    }
                    if (sub.Name == "group")        // sub.name 和sub.Name是不同的。
                    {
                        string name = sub.name;
                        string url = sub.location;
                        IList<string> components = ((string)sub.components).Split(',');
                        theTheme.Groups.Add( sub.name, components );
                    }
                }

                this.allThemes.Add(theTheme.Name, theTheme);

            }
        }

        // DynamicXmlNode实例的以节点名称命名的动态属性，既可能返回列表，也可能返回单个值（如果元素不重复）。
        // 下面的方法统一使之转化为列表的形式。
        private dynamic ConvertToList(dynamic node)
        {
            if (node == null)
                return new List<DynamicXmlNode>();

            if (node is DynamicXmlNode)
            {
                List<DynamicXmlNode> list = new List<DynamicXmlNode>();
                list.Add(node);
                return list;
            }
            else return node;
        }

        /// <summary>
        /// 读取指定的主题信息。
        /// </summary>
        /// <param name="themeName"></param>
        /// <returns></returns>
        public Theme GetTheme(string themeName)
        {
            if (allThemes.ContainsKey(themeName))
                return allThemes[themeName];
            else
                return allThemes[defaultTheme];
        }

        public Theme GetTheme()
        {
            return allThemes[defaultTheme];
        }

    } 
}
